package net.wicp.tams.common.metrics.core;

import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

import com.codahale.metrics.Counter;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.Meter;
import com.codahale.metrics.Metric;
import com.codahale.metrics.MetricFilter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;

import metrics_influxdb.HttpInfluxdbProtocol;
import metrics_influxdb.InfluxdbReporter;
import metrics_influxdb.InfluxdbReporter.Builder;
import net.wicp.tams.common.Conf;
import net.wicp.tams.common.apiext.StringUtil;
import net.wicp.tams.common.metrics.constant.ReportType;
import net.wicp.tams.common.metrics.utility.TsLogger;
import net.wicp.tams.common.metrics.utility.Utils;

public class TsMetricFacotory {

	private final static MetricRegistry metrics = new MetricRegistry();
	private final String factoryId;
	private final String IpAddress;
	private final String serviceName;

	static {
		ReportType sink = Conf.getEnum(ReportType.class, "common.metrics.sink");
		TimeUnit rateUnit = Conf.getEnum(TimeUnit.class, "common.metrics.rateUnit");
		Integer period = Conf.getInt("common.metrics.period");
		TimeUnit unit = Conf.getEnum(TimeUnit.class, "common.metrics.unit");
		TimeUnit durationsTo = Conf.getEnum(TimeUnit.class, "common.metrics.durationsTo");
		switch (sink) {
		case json:
			JsonReporter reporter = JsonReporter.forRegistry(metrics).outputTo(TsLogger.getTamsLogger()).convertRatesTo(rateUnit)
					.convertDurationsTo(durationsTo).build();
			reporter.start(period, unit);
			break;
		case influxdb:
			Builder influxdbBuilder = InfluxdbReporter.forRegistry(metrics);
			if (StringUtil.isNotNull(Conf.get("common.metrics.sink.influxdb.user"))
					&& StringUtil.isNotNull(Conf.get("common.metrics.sink.influxdb.password"))) {
				influxdbBuilder.protocol(new HttpInfluxdbProtocol(Conf.get("common.metrics.sink.influxdb.ip"),
						Conf.getInt("common.metrics.sink.influxdb.port"), Conf.get("common.metrics.sink.influxdb.user"),
						Conf.get("common.metrics.sink.influxdb.password"),
						Conf.get("common.metrics.sink.influxdb.defaultdb")));
			} else {
				influxdbBuilder.protocol(new HttpInfluxdbProtocol(Conf.get("common.metrics.sink.influxdb.ip"),
						Conf.getInt("common.metrics.sink.influxdb.port"),
						Conf.get("common.metrics.sink.influxdb.defaultdb")));
			}
			influxdbBuilder.convertRatesTo(rateUnit).convertDurationsTo(durationsTo).filter(MetricFilter.ALL)
					.skipIdleMetrics(false);
			Map<String, String> tags = Conf.getPre(Conf.get("common.metrics.sink.influxdb.tagPrefix"), true);
			for (String key : tags.keySet()) {
				influxdbBuilder.tag(key, tags.get(key));
			}
			influxdbBuilder.build().start(period, unit);
			break;
		default:
			break;
		}
	}

	public TsMetricFacotory(String serviceName) {
		this.serviceName = serviceName;
		factoryId = String.format("%d_%d", Utils.getPid(), System.currentTimeMillis());
		IpAddress = Utils.getLocalAddress();
	}

	protected String getIpAddress() {
		return IpAddress;
	}

	protected String getFactoryId() {
		return factoryId;
	}

	protected String getServiceName() {
		return serviceName;
	}

	protected MetricRegistry getMetrics() {
		return metrics;
	}

	public Counter newCounter(Class<?> tClass, String metricUniqMark) {
		String name = metricName(tClass, metricUniqMark);
		return metrics.counter(name);

	}
	public Counter newCounter(String metricName) {
		return metrics.counter(metricName);
	}
	

	public Histogram newHistogram(Class<?> tClass, String metricUniqMark) {
		String name = metricName(tClass, metricUniqMark);
		return metrics.histogram(name);
	}
	
	public Histogram newHistogram(String metricName) {
		return metrics.histogram(metricName);
	}

	public <T> Gauge<T> newGauge(Gauge<T> gauge, Class<?> tClass, String metricUniqMark) {
		String name = metricName(tClass, metricUniqMark);
		return metrics.register(name, gauge);
	}
	
	public <T> Gauge<T> newGauge(Gauge<T> gauge,String metricName) {
		return metrics.register(metricName, gauge);
	}
	

	public Meter newMeter(Class<?> tClass, String metricUniqMark) {
		String name = metricName(tClass, metricUniqMark);
		return metrics.meter(name);
	}
	
	public Meter newMeter(String metricName) {
		return metrics.meter(metricName);
	}
	

	public Timer newTimer(Class<?> tClass, String metricUniqMark) {
		String name = metricName(tClass, metricUniqMark);
		return metrics.timer(name);
	}
	
	public Timer newTimer(String metricName) {
		return metrics.timer(metricName);
	}
	

	protected String metricName(Class<?> tClass, String metricUniqMark) {
		String name = MetricRegistry.name(IpAddress, serviceName, String.valueOf(factoryId), tClass.getName(),
				metricUniqMark);
		return name;
	}

	public void destroyAllMetrics() {
		String rmNameTags = MetricRegistry.name(IpAddress, serviceName, String.valueOf(factoryId));
		removeMetrics(rmNameTags);
	}

	public void destroyMetricByMark(String metricUniqMark) {
		String rmNameTags = MetricRegistry.name(IpAddress, serviceName, String.valueOf(factoryId), ".*",
				metricUniqMark);
		removeMetrics(rmNameTags);
	}

	public void destroyMetric(final Metric pMetric) {
		metrics.removeMatching(new MetricFilter() {
			public boolean matches(String name, Metric metric) {
				return pMetric == metric;
			}
		});
	}

	private final void removeMetrics(String rmNameTags) {
		final Pattern rmPat = Pattern.compile(".*" + rmNameTags + ".*");
		metrics.removeMatching(new MetricFilter() {
			public boolean matches(String name, Metric metric) {
				return rmPat.matcher(name).matches();
			}
		});
	}

}
